package com.cwt.admin.common;

import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.apache.catalina.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author chenwt
 * @version V1.0
 * @describe 【测试注解校验权限】
 * 使用拦截器模式，只能在Controller层进行注解鉴权，如需在任意层级使用注解鉴权，请参考：AOP注解鉴权
 * @date 2023年07月17日 15:17:35
 */
@RestController
@RequestMapping
public class TestAuthController {

    // 登录校验：只有登录之后才能进入该方法
    @SaCheckLogin
    @RequestMapping("info")
    public String info() {
        return "查询用户信息";
    }

    // 角色校验：必须具有指定角色才能进入该方法
    @SaCheckRole("super-admin")
    @RequestMapping("add1")
    public String add1() {
        return "用户增加";
    }

    // 权限校验：必须具有指定权限才能进入该方法
    @SaCheckPermission("user-add")
    @RequestMapping("add2")
    public String add2() {
        return "用户增加";
    }

    // 二级认证校验：必须二级认证之后才能进入该方法
    @SaCheckSafe()
    @RequestMapping("add3")
    public String add3() {
        return "用户增加";
    }

    // Http Basic 校验：只有通过 Basic 认证后才能进入该方法
    @SaCheckBasic(account = "sa:123456")
    @RequestMapping("add4")
    public String add4() {
        return "用户增加";
    }

    // 校验当前账号是否被封禁 comment 服务，如果已被封禁会抛出异常，无法进入方法
    @SaCheckDisable("comment")
    @RequestMapping("send")
    public String send() {
        return "查询用户信息";
    }

    // 注解式鉴权：只要具有其中一个权限即可通过校验
    // SaMode.AND，标注一组权限，会话必须全部具有才可通过校验。
    // SaMode.OR，标注一组权限，会话只要具有其一即可通过校验。
    @RequestMapping("atJurOr")
    @SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR)
    public SaResult atJurOr() {
        return SaResult.data("用户信息");
    }

    // 角色权限双重 “or校验”：具备指定权限或者指定角色即可通过校验
    // 写法一：orRole = "admin"，代表需要拥有角色 admin 。
    // 写法二：orRole = {"admin", "manager", "staff"}，代表具有三个角色其一即可。
    // 写法三：orRole = {"admin, manager, staff"}，代表必须同时具有三个角色。
    @RequestMapping("userAdd")
    @SaCheckPermission(value = "user.add", orRole = "admin")
    public SaResult userAdd() {
        return SaResult.data("用户信息");
    }

    // 此接口加上了 @SaIgnore 可以游客访问
    // @SaIgnore 修饰方法时代表这个方法可以被游客访问，修饰类时代表这个类中的所有接口都可以游客访问。
    // @SaIgnore 具有最高优先级，当 @SaIgnore 和其它鉴权注解一起出现时，其它鉴权注解都将被忽略。
    // @SaIgnore 同样可以忽略掉 Sa-Token 拦截器中的路由鉴权，在下面的 [路由拦截鉴权] 章节中我们会讲到。
    @SaIgnore
    @RequestMapping("getList")
    public SaResult getList() {
        return SaResult.ok();
    }

    // 在 `@SaCheckOr` 中可以指定多个注解，只要当前会话满足其中一个注解即可通过验证，进入方法。
    @SaCheckOr(
            login = @SaCheckLogin,
            role = @SaCheckRole("admin"),
            permission = @SaCheckPermission("user.add"),
            safe = @SaCheckSafe("update-password"),
            basic = @SaCheckBasic(account = "sa:123456"),
            disable = @SaCheckDisable("submit-orders")
    )
    @RequestMapping("test")
    public SaResult test() {
        return SaResult.ok();
    }

    // 当前客户端只要有 [ login 账号登录] 或者 [user 账号登录] 其一，就可以通过验证进入方法。
    //         注意：`type = "login"` 和 `type = "user"` 是多账号模式章节的扩展属性，此处你可以先略过这个知识点。
    @SaCheckOr(
            login = { @SaCheckLogin(type = "login"), @SaCheckLogin(type = "user") }
    )
    @RequestMapping("test1")
    public SaResult test1() {
        return SaResult.ok();
    }

    // 当你在一个方法上写多个注解鉴权时，其默认就是要满足所有注解规则后，才可以进入方法，只要有一个不满足，就会抛出异常
    @SaCheckLogin
    @SaCheckRole("admin")
    @SaCheckPermission("user.add")
    @RequestMapping("test2")
    public SaResult test2() {
        return SaResult.ok();
    }

    // 获取 缓存数据
    @RequestMapping("session")
    public SaResult session() {
        // 在 Sa-Token 中，Session 分为三种，分别是：
        //      Account-Session: 指的是框架为每个 账号id 分配的 Session
        //      Token-Session: 指的是框架为每个 token 分配的 Session
        //      Custom-Session: 指的是以一个 特定的值 作为SessionId，来分配的 Session

        // 1、Account-Session

        // 获取当前账号 id 的 Account-Session (必须是登录后才能调用)
        StpUtil.getSession();

        // 获取当前账号 id 的 Account-Session, 并决定在 Session 尚未创建时，是否新建并返回
        StpUtil.getSession(true);

        // 获取账号 id 为 10001 的 Account-Session
        StpUtil.getSessionByLoginId(10001);

        // 获取账号 id 为 10001 的 Account-Session, 并决定在 Session 尚未创建时，是否新建并返回
        StpUtil.getSessionByLoginId(10001, true);

        // 获取 SessionId 为 xxxx-xxxx 的 Account-Session, 在 Session 尚未创建时, 返回 null
        StpUtil.getSessionBySessionId("xxxx-xxxx");

        // 2、Token-Session

        // 获取当前 Token 的 Token-Session 对象
        StpUtil.getTokenSession();

        // 获取指定 Token 的 Token-Session 对象
        StpUtil.getTokenSessionByToken("token");

        // 3、Custom-Session

        // 查询指定key的Session是否存在
        SaSessionCustomUtil.isExists("goods-10001");

        // 获取指定key的Session，如果没有，则新建并返回
        SaSessionCustomUtil.getSessionById("goods-10001");

        // 获取指定key的Session，如果没有，第二个参数决定是否新建并返回
        SaSessionCustomUtil.getSessionById("goods-10001", false);

        // 删除指定key的Session
        SaSessionCustomUtil.deleteSessionById("goods-10001");
        return SaResult.ok();
    }

    // 在 Session 上存取值
    // 以上三种 Session 均为框架设计概念上的区分，实际上在获取它们时，返回的都是 SaSession 对象，你可以使用以下 API 在 SaSession 对象上存取值：
    @RequestMapping("getAndSetInSession")
    public SaResult getAndSetInSession() {
        SaSession session = StpUtil.getSession();

        // 写值
        session.set("name", "zhang");

        // 取值
        session.get("name");

        // 取值 (指定默认值)
        session.get("name", "<defaultValue>");

        // 取值 (若无值则执行参数方法, 之后将结果保存到此键名下,并返回此结果   若有值则直接返回, 无需执行参数方法)
        session.get("name", () -> "...");

        // ---------- 数据类型转换： ----------
        session.getInt("age");         // 取值 (转int类型)
        session.getLong("age");        // 取值 (转long类型)
        session.getString("name");     // 取值 (转String类型)
        session.getDouble("result");   // 取值 (转double类型)
        session.getFloat("result");    // 取值 (转float类型)
        session.getModel("key", User.class); // 取值 (指定转换类型)
        session.getModel("key", User.class, null);  // 取值 (指定转换类型, 并指定值为Null时返回的默认值)

        // 是否含有某个key (返回 true 或 false)
        session.has("key");

        // 删值
        session.delete("name");

        // 清空所有值
        session.clear();

        // 获取此 Session 的所有key (返回Set<String>)
        session.keys();

        // 返回此 Session 的id
        session.getId();

        // 返回此 Session 的创建时间 (时间戳)
        session.getCreateTime();

        // 返回此 Session 会话上的底层数据对象（如果更新map里的值，请调用session.update()方法避免产生脏数据）
        session.getDataMap();

        // 将这个 Session 从持久库更新一下
        session.update();

        // 注销此 Session 会话 (从持久库删除此Session)
        session.logout();

        // 获取当前 Token 的匿名 Token-Session （可在未登录情况下使用的 Token-Session）
        StpUtil.getAnonTokenSession();
        return SaResult.ok();
    }

}
